﻿using Core.DataAccess.Model;
using Core.DataAccess.Model.iTool.Gateway;
using Core.Framework;
using Environment.Interfaces;
using HandlerContextOptions;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace Environment.Implements
{
    public class NodeService : INodeService
    {
        IHttpClientFactory httpClientFactory;
        public NodeService(IHttpClientFactory httpClientFactory)
        {
            this.httpClientFactory = httpClientFactory;
        }

        public async Task ModifyStatus(string ip, LinuxSystemStatus status)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var node = await context.Nodes.Where(item => item.IP == ip).FirstOrDefaultAsync();

                if (node != null)
                {
                    node.CPU = status.CPU;
                    node.MEM = status.MEM;
                    node.MEMUSAGE = status.MEMUSAGE;
                    node.MEMUSAGEString = status.MEMUSAGEString;
                    node.MEMTOTAL = status.MEMTOTAL;
                    node.MEMTOTALString = status.MEMTOTALString;
                    node.BLOCKIO = status.BLOCKIO;
                    node.BLOCKIOTOTAL = status.BLOCKIOTOTAL;
                    node.BLOCKIOUSAGE = status.BLOCKIOUSAGE;
                    node.Status = 2;
                    node.LastActiveTime = DateTime.UtcNow;
                    await context.SaveChangesAsync();
                }
            }
        }

        public async Task Reporting(string ip, LinuxProcessStatus status)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var instance = await context.ServiceInstances.FindAsync(status.ServiceInstanceId);

                if (status.CPU == -1)
                {
                    instance.Cpu = "0.00%";
                    instance.Memory = "0.00MiB";
                    instance.Status = 4;
                }
                else
                {
                    if (status.IsRunInDocker)
                    {
                        instance.Cpu = status.CPUString;
                        instance.Memory = status.MEMString;
                    }
                    else
                    {
                        instance.Cpu = status.CPUString;
                        // 获取系统内存占用
                        var node = await context.Nodes.FindAsync(instance.NodeId);
                        instance.Memory = Math.Round(status.MEM * node.MEMTOTAL / 1024, 2).ToString() + "MiB";
                    }
                    if (instance.Status <= 5)
                    {
                        instance.Status = 2;

                        if (instance.Title == "iTool-Server")
                        {
                            IToolCenter toolCenter = ProviderOfServices.GetService<IToolCenter>();
                            await toolCenter.IsAvailableClouds();
                        }
                    }
                    instance.LastCheckTime = DateTime.UtcNow;
                }

                await context.SaveChangesAsync();
            }
        }

        public async Task<List<ServiceInstance>> GetInstances(string ip)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var node = await context.Nodes.Select(item => new { item.NodeId, item.IP }).Where(item => item.IP == ip).FirstOrDefaultAsync();

                if (node != null)
                {
                    return await context.ServiceInstances.Where(item => new int[] { 2, 5, 6 }.Contains(item.Status) && node.NodeId == item.NodeId).ToListAsync() ?? new List<ServiceInstance>();
                }

                return new List<ServiceInstance>();
            }
        }

        public async Task<bool> AddNode(string ip)
        {
            try
            {
                await using (iToolGatewayContext context = new iToolGatewayContext())
                {
                    var node = await context.Nodes.Where(item => item.IP == ip).FirstOrDefaultAsync();

                    LoggerUtils.Write($"AddNode: {ip},{node.TryToJson()}", "log3");

                    if (node != null)
                    {
                        return true;
                    }
                    else
                    {
                        var model = new Node
                        {
                            IP = ip,
                            Status = 2,
                            CreateTime = DateTime.UtcNow
                        };

                        await context.AddAsync(model);

                        await context.SaveChangesAsync();

                        LoggerUtils.Write($"AddNode save: {ip},{model.NodeId}", "log3");


                        return model.NodeId > 0;
                    }
                }
            }
            catch (Exception ex)
            {
                        LoggerUtils.Write($"AddNode error: {ip},{ex.Message}", "log3");
                return false;
            }
        }

        public async Task<List<Node>> GetNodes(Pagination pagination)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var list = await context
                           .Nodes.PaginationAsync(pagination);

                var nodeIds = list.Select(item => item.NodeId);

                var services = await context.ServiceInstances.Where(item => nodeIds.Contains(item.NodeId)).AsNoTracking().ToListAsync();

                foreach (var item in list)
                {
                    if (item.ServiceInstances == null)
                        item.ServiceInstances = new List<ServiceInstance>();

                    foreach (var service in services)
                    {
                        if (service.NodeId == item.NodeId)
                        {
                            item.ServiceInstances.Add(service);
                        }
                    }

                }

                return list;
            }
        }

        public async Task<(bool, string)> ExcuterCommand(string ip, string cmd)
        {
            try
            {
                var result = await this.PostAsync(ip, "shell", new StringContent(new
                {
                    command = cmd
                }.TryToJson()));
                return (!result.Contains("Error"), result);
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        public async Task<(bool, string)> ExcuterCommand(string ip, string cmd, string dbType, int port, string pwd)
        {
            try
            {
                var result = await this.PostAsync(ip, "shell", new StringContent(new
                {
                    command = cmd,
                    dbType = dbType,
                    port = port,
                    pwd = pwd
                }.TryToJson()));

                return (!result.Contains("Error"), result);
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        public async Task<LinuxProcessStatus> ExcuterGetDokcerStatusByContainer(string ip, string container)
        {
            try
            {
                var result = await this.PostAsync(ip, $"Docker/{container}", new StringContent(string.Empty));
                return result?.TryToEntity<LinuxProcessStatus>();
            }
            catch (Exception)
            {
                return default;
            }
        }

        public async Task<LinuxProcessStatus> ExcuterGetLinuxProcessStatusByProcessName(string ip, string processName)
        {
            try
            {
                var result = await this.PostAsync(ip, $"Linux/{processName}", new StringContent(string.Empty));
                return result?.TryToEntity<LinuxProcessStatus>();
            }
            catch (Exception)
            {
                return default;
            }
        }

        public async Task<string> PostAsync(string ip, string url, StringContent content)
        {
            try
            {
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
                content.Headers.Add("seed", Md5Helper.Hash(AppSetting.ApplicationSeedString + ip));
                var result = await this.httpClientFactory.CreateClient().PostAsync($"http://{ip}:1998/{url}", content);
                var value = await result.Content.ReadAsStringAsync();
                LoggerUtils.Write($"PostAsync:{value},code:{result.StatusCode},url:{($"http://{ip}:1998/{url}")}, seed:{Md5Helper.Hash(AppSetting.ApplicationSeedString + ip)}");

                if (result.StatusCode != System.Net.HttpStatusCode.OK)
                {
                    throw new Exception(value);
                }

                return value;
            }
            catch (Exception ex)
            {
                LoggerUtils.Write("PostAsync", ex);
                throw ex;
            }
        }

        public async Task<List<ServiceInstance>> GetAllInstances()
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                return await context.ServiceInstances.ToListAsync();
                //return await context.ServiceInstances.Where(item => item.Status == 2).ToListAsync();
            }
        }

        public async Task<List<ServiceInstance>> GetAllInstancesOfStorageAndConnector()
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                return await context.ServiceInstances
                    .Where(item => item.Title == "iTool-FileApi" || item.Title == "iTool-Connector")
                    .Select(item => new ServiceInstance
                    {
                        ServiceInstanceId = item.ServiceInstanceId,
                        Title = item.Title,
                        NodeIP = item.NodeIP,
                        Status = item.Status,
                    })
                    .ToListAsyncOfAsNoTracking();
            }
        }

        public async Task Stoped(string ip)
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var node = await context.Nodes.Where(item => item.IP == ip & item.Status != 4).FirstOrDefaultAsync();

                if (node != null)
                {
                    node.Status = 4;
                    var services = await context.ServiceInstances.Where(item => item.NodeId == node.NodeId & item.Status != 4).ToListAsync();
                    foreach (var item in services)
                    {
                        item.Status = 5;
                    }
                    await context.SaveChangesAsync();
                }
            }
        }

        public async Task UpdateServiceStatus(string ip, ProcessStatusEnum processStatus)
        {

            LoggerUtils.Write($"Service UpdateStatus: {ip} - {processStatus}");

            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var serviceInstance = await context.ServiceInstances.Where(item => item.NodeIP == ip & item.Title == "iTool-Server").FirstOrDefaultAsync();

                if (serviceInstance != null)
                {
                    switch (processStatus)
                    {
                        case ProcessStatusEnum.Stoping:
                        case ProcessStatusEnum.Staring:
                            serviceInstance.Status = 6;
                            serviceInstance.CreateTime = DateTime.UtcNow;
                            break;
                        case ProcessStatusEnum.Started:
                            serviceInstance.Status = 2;
                            break;
                        case ProcessStatusEnum.Stoped:
                        case ProcessStatusEnum.StartError:
                            serviceInstance.Status = 4;
                            break;
                        case ProcessStatusEnum.WaitingStopSignal:
                            serviceInstance.Status = 11;
                            break;
                        case ProcessStatusEnum.WaitingStartSignal:
                            serviceInstance.Status = 12;
                            break;
                        default:
                            return;
                    }
                    serviceInstance.LastCheckTime = DateTime.UtcNow;
                    await context.SaveChangesAsync();
                    if (!new int[] { 2, 11, 12 }.Contains(serviceInstance.Status))
                    {
                        return;
                    }
                }
            }

            await Task.Delay(2000);

            List<string> Ips = new List<string>();
            int staringInstanceCount = 0;
            string route = "iTool/service_BeginStop";

            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                switch (processStatus)
                {
                    case ProcessStatusEnum.Started:

                        // 开始启动客户端
                        staringInstanceCount = await context.ServiceInstances.Where(item => item.Title == "iTool-Connector" && item.Status == 12).CountAsync();
                        if (staringInstanceCount > 0)
                        {
                            staringInstanceCount = await context.ServiceInstances.Where(item => item.Title == "iTool-Server" && item.Status == 2).CountAsync();
                            if (staringInstanceCount > 0)
                            {
                                route = "iTool/client_BeginStart";
                                Ips = await context.ServiceInstances.Where(item => item.Title == "iTool-Connector" && item.Status == 12).Select(item => item.NodeIP).ToListAsync();
                            }
                        }
                        break;

                    case ProcessStatusEnum.WaitingStopSignal:
                        staringInstanceCount = await context.ServiceInstances.Where(item => item.Title == "iTool-Connector" && new int[] { 2, 11 }.Contains(item.Status)).CountAsync();
                        // 如果没有正常运行和等待客户端（说明客户端，还没有停止完全），准备下发停止信号
                        if (staringInstanceCount < 1)
                        {
                            Ips = await context.ServiceInstances.Where(item => item.Title == "iTool-Server").Select(item => item.NodeIP).ToListAsync();
                        }
                        break;
                    case ProcessStatusEnum.WaitingStartSignal:
                        // 如果有服务端在准备启动状态，则下发启动信号
                        route = "iTool/service_BeginStart";
                        Ips = await context.ServiceInstances.Where(item => item.Title == "iTool-Server").Select(item => item.NodeIP).ToListAsync();

                        // 等待服务没有启动状态，并且没有等待状态的服务端 才可以开始启动
                        // 停止检查 - 如果没有正在工作的机器
                        // 则下发停止信号
                        break;
                }
            }

            if (Ips.Any())
            {
                LoggerUtils.Write($"call server ： {processStatus} - {route}", "log_service");

                // 下发信号
                foreach (var itemIp in Ips)
                {
                    await this.PostAsync(itemIp, route, new StringContent(string.Empty));
                }
            }
        }

        public async Task UpdateClientStatus(string ip, ProcessStatusEnum processStatus)
        {
            LoggerUtils.Write($"Client UpdateStatus: {ip} - {processStatus}");

            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var serviceInstance = await context.ServiceInstances.Where(item => item.NodeIP == ip & item.Title == "iTool-Connector").FirstOrDefaultAsync();

                if (serviceInstance != null)
                {
                    switch (processStatus)
                    {
                        case ProcessStatusEnum.Stoping:
                        case ProcessStatusEnum.Staring:
                            serviceInstance.Status = 6;
                            serviceInstance.CreateTime = DateTime.UtcNow;
                            break;
                        case ProcessStatusEnum.Started:
                            serviceInstance.Status = 2;
                            break;
                        case ProcessStatusEnum.Stoped:
                        case ProcessStatusEnum.StartError:
                            serviceInstance.Status = 4;
                            break;
                        case ProcessStatusEnum.WaitingStopSignal:
                            serviceInstance.Status = 11;
                            break;
                        case ProcessStatusEnum.WaitingStartSignal:
                            serviceInstance.Status = 12;
                            break;
                        default:
                            return;
                    }
                    serviceInstance.LastCheckTime = DateTime.UtcNow;
                    await context.SaveChangesAsync();

                    if (!new int[] { 11, 12 }.Contains(serviceInstance.Status))
                    {
                        return;
                    }
                }
            }

            await Task.Delay(2000);

            List<string> Ips = new List<string>();
            int staringInstanceCount = 0;
            string route = "iTool/client_BeginStop";

            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                switch (processStatus)
                {
                    case ProcessStatusEnum.WaitingStopSignal:
                        staringInstanceCount = await context.ServiceInstances.Where(item => item.Title == "iTool-Connector" && item.Status == 2).CountAsync();
                        // 如果没有正常运行的客户端(说明所有客户端都是等待停止信号)，准备下发停止信号
                        if (staringInstanceCount < 1)
                        {
                            Ips = await context.ServiceInstances.Where(item => item.Title == "iTool-Connector").Select(item => item.NodeIP).ToListAsync();
                        }
                        break;
                    case ProcessStatusEnum.WaitingStartSignal:
                        staringInstanceCount = await context.ServiceInstances.Where(item => item.Title == "iTool-Server" && item.Status == 2).CountAsync();
                        if (staringInstanceCount > 0)
                        {
                            // 如果 有服务端 已经 启动，准备 下发 启动信号
                            route = "iTool/client_BeginStart";
                            Ips = await context.ServiceInstances.Where(item => item.Title == "iTool-Connector").Select(item => item.NodeIP).ToListAsync();
                        }
                        else
                        {
                            // 否则 应该 下发服务端 开始停止信号
                            route = "iTool/service_BeginStop";
                            Ips = await context.ServiceInstances.Where(item => item.Title == "iTool-Server" & item.Status < 12).Select(item => item.NodeIP).ToListAsync();
                        }

                        // 等待服务没有启动状态，并且没有等待状态的服务端 才可以开始启动
                        // 停止检查 - 如果没有正在工作的机器
                        // 则下发停止信号
                        break;
                }
            }

            if (Ips.Any())
            {
                LoggerUtils.Write($"call client ： {processStatus} - {route}", "log_service");

                // 下发信号
                foreach (var itemIp in Ips)
                {
                    await this.PostAsync(itemIp, route, new StringContent(string.Empty));
                }
            }


        }

        public async Task<List<Node>> GetNodes()
        {
            await using (iToolGatewayContext context = new iToolGatewayContext())
            {
                var nodes = await context.Nodes.Where(item => item.Status == 2 && item.LastActiveTime > DateTime.UtcNow.AddMinutes(-5)).ToListAsync();
                return nodes ?? new List<Node>();
            }
        }
    }
}
